//
//  PortfolioUploader.m
//  Moo-O Touch
//
//  Created by Reisen on 8/21/13.
//
//

#import "FileUploader.h"
#import "Utility.h"
#import "TalkDefine.h"
 
@implementation UploadInfo

-(id) initWithPath:(NSString*)filePath
{
    self = [super init];
    
    if(self)
    {
        self.contentPath = filePath;
        self.fileName = [filePath lastPathComponent];
        self.contentLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
        
        self.contentType = @"application/octet-stream";
        self.offset = 0;
        
        TLDebugS(@"Content path = %@", filePath);
        TLDebugS(@"Content length = %ld", self.contentLength);
    }
    
    return self;
}
@end


@implementation FileUploader

-(id) init
{
    self = [super init];
    
    if(self)
    {
        //  self.username = @"haipt2012@gmail.com";
        //   self.password = @"63f133f34287020469024c04461ceccd";
//        self.serverURL = @"http://218.244.135.77/";
        self.serverURL = @"http://talk-joyreader.wawayaya.com/";
        
        self.pendingUploadDicts = [NSMutableArray array];
        self.isBusy = NO;
    }
    
    return self;
}
//public bool UploadAsync
-(void) uploadFile:(NSString*)filePath
{
    [self uploadFiles:[NSMutableArray arrayWithObjects:filePath, nil]];
}

-(void) uploadFiles:(NSMutableArray*)files
{
    [self uploadFiles:files threaded:YES];
}

-(void) uploadFiles:(NSMutableArray*)files threaded:(BOOL)threaded
{
    self.isCancelling = NO;
    self.pendingUploads = files;
    
    self.curProgress = 0;
    self.maxProgress = 0;
    self.isBusy = YES;
    
    for(NSString* path in files)
    {
        if(![[NSFileManager defaultManager] fileExistsAtPath:path])
            continue;
        
        self.maxProgress += [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileSize];
    }
    
    if(threaded)
    {
        self.workerThread = [[NSThread alloc] initWithTarget:self selector:@selector(uploadInThreadMaster) object:nil];
        [self.workerThread start];
    }
    
    else [self uploadInThreadMaster];
}

-(void) uploadInThreadMaster
{
    self.isBusy = YES;
    
    while (self.pendingUploads.count > 0) {
        NSString* toUpload = self.pendingUploads.lastObject;
        [self.pendingUploads removeLastObject];
        
        if(![[NSFileManager defaultManager] fileExistsAtPath:toUpload])
        {
            TLDebugS(@"File not exists:%@!", toUpload);
            continue;
        }
        
        @synchronized(self.clientFileInfo)
        {
            self.clientFileInfo = [[UploadInfo alloc] initWithPath:toUpload];
            
            if(![self uploadInThread])
            {
                self.status = @"fail";
                self.message = @"Unstable connection, please try again later!";
                
                dispatch_async(dispatch_get_main_queue(), ^(){
                [self.delegate uploadFailed:self];
                });
                
                self.isBusy = NO;
                self.isCancelling = NO;
                return;
            }
        }
    }
    
    if(self.isCancelling)
    {
        TLDebugS(@"Cancel!");
        
        self.status = @"fail";
        self.message = @"Cancelled by user.";
        
        dispatch_async(dispatch_get_main_queue(), ^(){
            [self.delegate uploadFailed:self];
        });
    }
    
    else
    {
        TLDebugS(@"Done!");
        
        dispatch_async(dispatch_get_main_queue(), ^(){
        [self.delegate uploadCompleted:self];
        });
    }
    
    self.isBusy = NO;
    self.isCancelling = NO;
}

//protected void UploadThread
-(BOOL) uploadInThread
{
    //New
    if(self.clientFileInfo.guid == nil)
    {
        //UploadInit callback
        TLDebugS(@"UploadInit!");
        
        if(![self newUploadSession])
        {
            TLDebugS(@"Error!");
            return NO;
        }
        
        //UploadStarted callback
        TLDebugS(@"UploadStarted!");
    }
    
    //Resume
    else
    {
        
    }
    
    //Do upload
    NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:self.clientFileInfo.contentPath];
    [handle seekToFileOffset:self.clientFileInfo.offset];
    
    long segmentSize = 512 * 1024;
    
    int attempt = 0;
    int maxAttempt = 10;
    
    while (self.clientFileInfo.offset < self.clientFileInfo.contentLength && !self.isCancelling)
    {
        @autoreleasepool {
            if(self.isCancelling)
                break;
            
            long remaining = self.clientFileInfo.contentLength - self.clientFileInfo.offset;
            long dataSize = MIN(remaining, segmentSize);
            
            NSData* tmpData = [handle readDataOfLength:dataSize];
            TLDebugS(@"Handle offset = %lld", handle.offsetInFile);
            
            attempt = 0;
            
            while(![self uploadData:tmpData])
            {
                TLDebugS(@"Errur!");
                attempt++;
                
                if(attempt == maxAttempt)
                    break;
            }
            
            if(attempt == maxAttempt)
                break;
            
            self.clientFileInfo.offset += dataSize;
            self.curProgress += dataSize;
            //ProgressUpdated callback
            
            float progress = 0;
            
            if(self.maxProgress > 0)
                progress = 1.0f * self.curProgress / self.maxProgress;
            
            dispatch_async(dispatch_get_main_queue(), ^(){
                [self.delegate uploadProgress:progress];
            });
        }
    }
    
    if(attempt == maxAttempt)
    {
        TLDebugS(@"Report error!");
        
        return NO;
    }
    
    if(self.isCancelling)
    {
        //UploadCancelled callback
        return NO;
    }
    
    else if(self.clientFileInfo.offset == self.clientFileInfo.contentLength)
    {
        TLDebugS(@"Upload completed!");
        
        self.url = nil;
        
        NSMutableDictionary* dict = [NSMutableDictionary dictionary];
        [dict setObject:@"video" forKey:@"type"];
        [dict setObject:self.clientFileInfo.link forKey:@"files"];
        
        NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.dateFormat = @"dd/MM/yyyy HH:mm:ss";
        
        dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
        NSString* strTime = [dateFormatter stringFromDate:[NSDate date]];
        
        [dict setObject:strTime forKey:@"created_time"];
        
        NSString* param = [NSString stringWithFormat:@"{\"type\":\"video\",\"files\":[\"%@\"],\"captured\":\"%@\"}", self.clientFileInfo.link, strTime];
        
        NSData* response_str = [Utility postRequestWithString: [self.serverURL stringByAppendingString:@"api/post_file"] :param];
        
        TLDebugS(@"Response = %@", [[NSString alloc] initWithData:response_str encoding:NSUTF8StringEncoding]);
        
        
        if(response_str != nil)
        {
            NSDictionary* json = [NSJSONSerialization JSONObjectWithData:response_str options:kNilOptions error:nil];

            self.status = [json objectForKey:@"status"];

            if([self.status isEqualToString:@"ok"])
                self.url = [json objectForKey:@"url"];
            
            else
                self.message = [json objectForKey:@"msg"];
        }
        
        else
        {
            self.status = @"fail";
            self.message = @"Couldn't post.";
        }
        //Completed callback
        return YES;
    }
    
    return NO;
}

//protected bool NewUploadSession
-(BOOL) newUploadSession
{
    self.username = [self.username stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];

    NSString* url = [self.serverURL stringByAppendingFormat:@"files/create?machine_id=%lld&username=%@&user_id=%lld&app_secret=b10a8db164e0754105b7a99be72e3fe5", self.uId, self.username, self.uId];

//    NSString* url = [self.serverURL stringByAppendingFormat:@"files/create?mid=%@&machine_id=%lld&app_secret=b10a8db164e0754105b7a99be72e3fe5", self.username, self.uId];

    
    TLDebugS(@"New upload: %@ size = %ld", self.clientFileInfo.contentPath, self.clientFileInfo.contentLength);
    
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    
    NSString* path = self.clientFileInfo.contentPath;
    [dict setObject:[NSString stringWithFormat:@"%ld", self.clientFileInfo.contentLength]  forKey:@"file_size"];
    
    NSString* utf8 = self.clientFileInfo.fileName;
    [dict setObject:utf8 forKey:@"file_name"];
    NSDate* creationDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileModificationDate];
    
    if(creationDate == nil) creationDate = [NSDate date];
    
    NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    
    dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
    NSString* strTime = [dateFormatter stringFromDate:creationDate];
    
    [dict setObject:strTime forKey:@"created_time"];
    
    [self addAuthentication:dict];
    
    NSData* response_str = [Utility postRequest:url :dict];
    
    if(response_str == nil)
    {
        TLDebugS(@"FAIL");
        
        self.status = @"fail";
        self.message = @"Network error!";
        
        dispatch_async(dispatch_get_main_queue(), ^(){
        [self.delegate uploadFailed:self];
        });
        
        return NO;
    }
    
    TLDebugS(@"Response = %@", [[NSString alloc] initWithData:response_str encoding:NSUTF8StringEncoding]);
    
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:response_str options:kNilOptions error:nil];
    
    if([[json objectForKey:@"status"] isEqualToString:@"ok"])
    {
        self.clientFileInfo.guid = [json objectForKey:@"GUID"];
        self.clientFileInfo.link = [json objectForKey:@"download_link"];
        
        TLDebugS(@"GUID = %@, Link = %@", self.clientFileInfo.guid, self.clientFileInfo.link);
        
        return YES;
    }
    
    else
    {
        dispatch_async(dispatch_get_main_queue(), ^(){
        [self.delegate uploadFailed:self];
        });
        TLDebugS(@"Failed NewUploadSession:%@", [json objectForKey:@"error"]);
    }
    return NO;
}

//private bool UploadData(data)
-(BOOL) uploadData:(NSData*)data
{
    NSString* hostUrl = [NSString stringWithFormat:@"%@files/%@/upload_part/%ld", self.serverURL, self.clientFileInfo.guid, self.clientFileInfo.offset];
    
    NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:hostUrl]];
    request.HTTPMethod = @"POST";
    [request setValue:@"text/html,application/xhtml+xml,*/*" forHTTPHeaderField:@"Accept"];
    NSString* boundary = [NSString stringWithFormat:@"----------%f", [[NSDate date] timeIntervalSince1970] * 1000];
    
    [request setValue:[@"multipart/form-data; boundary=" stringByAppendingString:boundary] forHTTPHeaderField:@"Content-Type"];
    
    NSString* header = @"";
    header = [header stringByAppendingFormat:@"--%@\r\n", boundary];
    header = [header stringByAppendingFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", @"file", [self.clientFileInfo.contentPath lastPathComponent]];
    header = [header stringByAppendingFormat:@"Content-Type: %@\r\n\r\n", self.clientFileInfo.contentType];
    NSData* headerData = [header dataUsingEncoding:NSUTF8StringEncoding];
    
    
    NSString* footer = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary];
    NSData* footerData = [footer dataUsingEncoding:NSUTF8StringEncoding];
    
    
    
    NSMutableData* bodyData = [NSMutableData data];
    [bodyData appendData:headerData];
    [bodyData appendData:data];
    [bodyData appendData:footerData];
    
    request.HTTPBody = bodyData;
    [request setValue:[NSString stringWithFormat:@"%zd", bodyData.length] forHTTPHeaderField:@"Content-Length"];
    
    
    NSURLResponse* res = nil;
    NSError* error = nil;
    
    NSData* resData = [NSURLConnection sendSynchronousRequest:request returningResponse:&res error:&error];
    
    if(resData == nil)
    {
        self.status = @"fail";
        self.message = @"Network error!";
        
        return NO;
    }
    
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:resData options:kNilOptions error:nil];
    if([[json objectForKey:@"status"] isEqualToString:@"ok"])
    {
        TLDebugS(@"Upload success!");
        
        return YES;
    }
    
    else
    {
        TLDebugS(@"Failed Upload:%@", [[NSString alloc] initWithData:resData encoding:NSUTF8StringEncoding]);
        return NO;
    }
}

-(NSString*) addAuthenticationToString:(NSString*)url
{
    return url;
}

-(void) addAuthentication:(NSMutableDictionary*)dict
{
}

-(void) cancel
{
    if(self.isBusy) self.isCancelling = YES;
}
@end